package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.config.CacheService;
import com.heima.article.mapper.ApArticleConfigMapper;
import com.heima.article.mapper.ApArticleContentMapper;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.mapper.ApAuthorMapper;
import com.heima.article.service.ApArticleService;
import com.heima.common.constants.article.ArticleConstants;
import com.heima.common.constants.message.ArticleConstans;
import com.heima.common.constants.message.ArticleVisitStreamMess;
import com.heima.common.exception.config.CustException;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.dtos.HotArticleVo;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.pojos.ApArticleConfig;
import com.heima.model.article.pojos.ApArticleContent;
import com.heima.model.article.pojos.ApAuthor;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 文章信息表，存储已发布的文章 服务实现类
 * </p>
 *
 * @author itheima
 */
@Slf4j
@Service
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {


    @Autowired
    ApAuthorMapper apAuthorMapper;

    @Autowired
    ApArticleContentMapper apArticleContentMapper;

    @Autowired
    ApArticleConfigMapper apArticleConfigMapper;


    @Autowired
    ApArticleMapper apArticleMapper;

    @Value("${file.oss.web-site}")
    private String webSite;

    @Autowired
    CacheService cacheService;


    /**
     * 更新文章行为数据及缓存
     * @param streamMess
     */
    @Override
    public void updateApArticle(ArticleVisitStreamMess streamMess) {
        //1 查询文章数据
        if (streamMess.getArticleId() == null) {
            log.info("updateApArticle getArticleId is null");
            return;
        }
        ApArticle apArticle = apArticleMapper.selectById(streamMess.getArticleId());
        if (apArticle == null) {
            log.info("updateApArticle selectById is null id:{}", streamMess.getArticleId());
            return;
        }

        //2 计算分数（*3）
        int score = apArticlecomputeScore(apArticle);
        score = score * 3;


        //3 更新文章4大行为
        apArticle = handleBehaviorData(apArticle, streamMess);
        updateById(apArticle);


        //4 更新当前文章对应频道下的缓存
        String articleListStr = cacheService.get(ArticleConstans.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId());
        updateArticleCache(apArticle, score, articleListStr,
                ArticleConstans.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId());

        //5 更新推荐的缓存文章
        articleListStr = cacheService.get(ArticleConstans.HOT_ARTICLE_FIRST_PAGE + ArticleConstans.DEFAULT_TAG);
        updateArticleCache(apArticle, score, articleListStr,
                ArticleConstans.HOT_ARTICLE_FIRST_PAGE + ArticleConstans.DEFAULT_TAG );
    }

    /**
     * 更新Redis缓存中的文章数据
     * @param apArticle  当前文章
     * @param score 文章分数
     * @param articleListStr 缓存中的文章（30条 已排序）
     * @param key 缓存key
     */
    private void updateArticleCache(ApArticle apArticle, int score, String articleListStr, String key) {

        log.info("updateApArticle updateArticleCache apArticle:{},score:{}",apArticle,score);
        // 转集合 （30条 已排序）
        List<HotArticleVo> hotArticleVos = JSON.parseArray(articleListStr, HotArticleVo.class);

        if (hotArticleVos == null) {
            hotArticleVos = new ArrayList<>();
        }

        HotArticleVo hotArticleVo = new HotArticleVo();
        BeanUtils.copyProperties(apArticle, hotArticleVo);
        hotArticleVo.setScore(score);

        if (hotArticleVos.size() < 30) {
            hotArticleVos.add(hotArticleVo);
        }else {

            // 缓存中是否存在当前文章 存在则 更新分数
            boolean flag = false;
            for (HotArticleVo articleVo : hotArticleVos) {
                if (apArticle.getId().equals(articleVo.getId())) {
                    articleVo.setScore(articleVo.getScore() + score);
                    flag = true;
                    break;
                }
            }

            // 不存在 比较集合最后一条记录的分数
            if (!flag) {

                //*** 排序
                hotArticleVos = hotArticleVos.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed()).collect(Collectors.toList());
                // 取最后一条
                HotArticleVo lastHotArticleVo = hotArticleVos.get(hotArticleVos.size() - 1);

                if (lastHotArticleVo.getScore() < score) { // 当前文章的分值大
                    // 删除集合最后一条
                    hotArticleVos.remove(lastHotArticleVo);

                    // 添加当前文章vo
                    hotArticleVos.add(hotArticleVo);
                }
            }

        }

        //**** 重新排序 ***
        hotArticleVos = hotArticleVos.stream()
                .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                .limit(30)
                .collect(Collectors.toList());

        // 更新缓存
        cacheService.set(key, JSON.toJSONString(hotArticleVos));

        log.info("updateApArticle updateArticleCache success");
    }

    /**
     * 更新文章的行为
     * @param apArticle
     * @return
     */
    private ApArticle handleBehaviorData(ApArticle apArticle, ArticleVisitStreamMess mess) {

        // 阅读
        if (mess.getView() != 0) {
            int views = (int) (apArticle.getViews() == null ?
                    mess.getView() : apArticle.getViews() + mess.getView());
            apArticle.setViews(views );
        }

        //点赞
        if (mess.getLike() != 0) {
            int like = (int) (apArticle.getLikes() == null ?
                    mess.getLike() : mess.getLike() + apArticle.getLikes());
            apArticle.setLikes(like);
        }
        // 评论
        if (mess.getComment() != 0) {
            int comment = (int) (apArticle.getComment() == null ?
                    mess.getComment() : mess.getComment() + apArticle.getComment());
            apArticle.setComment(comment);
        }
        // 收藏
        if (mess.getCollect() != 0) {
            int collection = (int) (apArticle.getCollection() == null ?
                    mess.getCollect() : mess.getCollect() + apArticle.getCollection());
            apArticle.setCollection(collection);
        }
        return apArticle;
    }


    /**
     * 计算文章分数
     * （阅读：1，点赞：3，评论：5，收藏：8）
     * @param apArticle
     * @return
     */
    private int apArticlecomputeScore(ApArticle apArticle) {

        int score = 0;
        //阅读
        if (apArticle.getViews() != null) {
            score += apArticle.getViews() * ArticleConstans.HOT_ARTICLE_VIEW_WEIGHT;
        }

        //点赞
        if (apArticle.getLikes() != null) {
            score += apArticle.getLikes() * ArticleConstans.HOT_ARTICLE_LIKE_WEIGHT;
        }

        // 评论 5
        if (apArticle.getComment() != null) {
            score += apArticle.getComment() * ArticleConstans.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        // 收藏 8
        if (apArticle.getCollection() != null) {
            score += apArticle.getCollection() * ArticleConstans.HOT_ARTICLE_COLLECTION_WEIGHT;
        }

        return score;
    }

    /**
     * 根据参数加载文章列表 redis
     *
     * @param loadtype 1为加载更多  2为加载最新
     * @param dto
     * @return
     */
    @Override
    public ResponseResult load2(Short loadtype, ArticleHomeDto dto, boolean firstPage) {

        // redis
        if (firstPage) {
            String listStr = cacheService.get(ArticleConstans.HOT_ARTICLE_FIRST_PAGE + dto.getTag());
            if (StringUtils.isNotBlank(listStr)) {
                List<HotArticleVo> articleVos = JSON.parseArray(listStr, HotArticleVo.class);
                return ResponseResult.okResult(articleVos);
            }
        }

        // 数据库
        return load(loadtype, dto);
    }

    /**
     * 根据参数加载文章列表
     *
     * @param loadtype 1为加载更多  2为加载最新
     * @param dto
     * @return
     */
    @Override
    public ResponseResult load(Short loadtype, ArticleHomeDto dto) {
        //1 参数校验
        if (dto == null) {
            dto = new ArticleHomeDto();
        }
        // size
        Integer size = dto.getSize();
        if (size == null || size <= 0 || size >= 50) {
            size = 30;
        }
        dto.setSize(size);
        // 时间
        if (dto.getMinBehotTime() == null) {
            dto.setMinBehotTime(new Date());
        }
        if (dto.getMaxBehotTime() == null) {
            dto.setMaxBehotTime(new Date());
        }
        // 频道
        if (StringUtils.isBlank(dto.getTag())) {
            dto.setTag(ArticleConstants.DEFAULT_TAG);
        }
        // 类型type
        if (!loadtype.equals(ArticleConstants.LOADTYPE_LOAD_MORE)
                && !loadtype.equals(ArticleConstants.LOADTYPE_LOAD_NEW)) {
            loadtype = ArticleConstants.LOADTYPE_LOAD_MORE;
        }

        //2 执行查询
        List<ApArticle> articles = apArticleMapper.loadArticleList(dto, loadtype);


        //3 返回结果
        ResponseResult result = ResponseResult.okResult(articles);
        result.setHost(webSite);
        return result;
    }

    /**
     * 保存文章三张表的数据
     * @param articleDto 文章数据
     * @return data设置文章的id
     */
    @Override
    public ResponseResult saveArticle(ArticleDto articleDto) {
        //1 参数校验
        if (articleDto == null || StringUtils.isBlank(articleDto.getContent())) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID, "文章参数非法");
        }
        ApArticle apArticle = new ApArticle();
        BeanUtils.copyProperties(articleDto, apArticle);

        //2 补全作者id
        String authorName = articleDto.getAuthorName();
        if (StringUtils.isNotBlank(authorName)) {
            ApAuthor apAuthor = apAuthorMapper.selectOne(Wrappers.<ApAuthor>lambdaQuery().eq(ApAuthor::getName, authorName));
            if (apAuthor != null) {
                apArticle.setAuthorId(apAuthor.getId());
            }
        }

        //3 判断文章id是否存在， 不存在则保存， 存在修改
        if (apArticle.getId() == null) { // 新增
            save(apArticle);
        }else { // 更新

            ApArticle article = getById(apArticle.getId());
            if (article == null) {
                CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST, "文章数据已删除");
            }
            // 根据文章的id删除ArticleContent和ArticleConfig表数据
            apArticleContentMapper.delete(Wrappers.<ApArticleContent>lambdaQuery()
                    .eq(ApArticleContent::getArticleId, apArticle.getId()));
            apArticleConfigMapper.delete(Wrappers.<ApArticleConfig>lambdaQuery()
                    .eq(ApArticleConfig::getArticleId, apArticle.getId()));


            updateById(apArticle);
        }
        //4 保存另外两张表数据
        //添加文章配置
        //保存article_config 数据
        ApArticleConfig apArticleConfig = new ApArticleConfig();
        apArticleConfig.setIsForward(true); // 转发
        apArticleConfig.setIsComment(true); // 评论
        apArticleConfig.setIsDown(false); // 是否下架
        apArticleConfig.setIsDelete(false); // 是否删除
        apArticleConfig.setArticleId(apArticle.getId());
        apArticleConfigMapper.insert(apArticleConfig);

        //添加文章内容
        //保存文章article_content 数据
        ApArticleContent apArticleContent = new ApArticleContent();
        apArticleContent.setContent(articleDto.getContent());
        apArticleContent.setArticleId(apArticle.getId());
        apArticleContentMapper.insert(apArticleContent);

        //5 返回结果（文章ID）
        return ResponseResult.okResult(apArticle.getId());
    }
}
